package com.bosssoft.platform.fasttcc.support.feign;

import com.bosssoft.platform.fasttcc.TccTransaction;
import com.bosssoft.platform.fasttcc.TccTransactionManager;
import com.bosssoft.platform.fasttcc.rpc.command.TccMethodCallCommand;
import com.bosssoft.platform.fasttcc.rpc.filter.RpcFilter;
import com.jfireframework.baseutil.TRACEID;
import com.jfireframework.baseutil.reflect.ReflectUtil;
import feign.Client;
import feign.Request;
import feign.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashSet;
import java.util.Set;

@Component
public class FeignClientPostProcessor implements BeanPostProcessor
{
    private static final Logger                LOGGER = LoggerFactory.getLogger(FeignClientPostProcessor.class);
    private static       Field                 delegate;
    @Resource
    private              RpcFilter             rpcFilter;
    @Resource
    private              TccTransactionManager tccTransactionManager;

    static
    {
        try
        {
            delegate = LoadBalancerFeignClient.class.getDeclaredField("delegate");
            Field modifiersField = Field.class.getDeclaredField("modifiers");
            modifiersField.setAccessible(true);
            modifiersField.setInt(delegate, delegate.getModifiers() & ~Modifier.FINAL);
            delegate.setAccessible(true);
        }
        catch (NoSuchFieldException | IllegalAccessException e)
        {
            ReflectUtil.throwException(e);
        }
    }

    static final String CALLID         = "FASTTCC-CALLID";
    static final String NODEIDENTIFIER = "FASTTCC-NODEIDENTIFIER";
    static final String XID            = "FASTTCC-XID";

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException
    {
        if (bean instanceof LoadBalancerFeignClient)
        {
            LoadBalancerFeignClient loadBalancerFeignClient = (LoadBalancerFeignClient) bean;
            try
            {
                final Client client = (Client) delegate.get(loadBalancerFeignClient);
                Client myInteceptor = new Client()
                {
                    @Override
                    public Response execute(Request request, Request.Options options) throws IOException
                    {
                        String         traceId               = TRACEID.currentTraceId();
                        TccTransaction currentTccTransaction = tccTransactionManager.getCurrentTccTransaction();
                        if (currentTccTransaction == null)
                        {
                            LOGGER.debug("traceId:{} 本次远端访问不在TCC事务作用域下，忽略", traceId);
                            return client.execute(request, options);
                        }
                        try
                        {
                            URI                  uri                  = new URI(request.url());
                            String               host                 = uri.getHost();
                            int                  port                 = uri.getPort();
                            String               identifier           = host + ":" + port + ":unknow";
                            TccMethodCallCommand tccMethodCallCommand = rpcFilter.beforeSendRequest(identifier);
                            if (tccMethodCallCommand == null)
                            {
                                LOGGER.debug("traceId:{} 本次远端访问不在TCC事务作用域下，忽略", traceId);
                                return client.execute(request, options);
                            }
                            Set<String> value = new HashSet<>();
                            value.add(tccMethodCallCommand.getXid().toString());
                            request.headers().put(XID, value);
                            value = new HashSet<>();
                            value.add(tccMethodCallCommand.getCallId());
                            request.headers().put(CALLID, value);
                            value = new HashSet<>();
                            value.add(tccMethodCallCommand.getNodeIdentifier());
                            request.headers().put(NODEIDENTIFIER, value);
                            LOGGER.debug("traceId:{} 本次远端请求在TCC作用域下，CallID:{},Xid:{}", traceId, tccMethodCallCommand.getCallId(), tccMethodCallCommand.getXid().toString());
                            return client.execute(request, options);
                        }
                        catch (URISyntaxException e)
                        {
                            ReflectUtil.throwException(e);
                            return null;
                        }
                    }
                };
                delegate.set(loadBalancerFeignClient, myInteceptor);
                return loadBalancerFeignClient;
            }
            catch (IllegalAccessException e)
            {
                ReflectUtil.throwException(e);
                return null;
            }
        }
        else
        {
            return bean;
        }
    }
}
